package com.scholarship.common.config;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import javax.servlet.Filter;
import java.util.Base64;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Shiro配置类
 * Subject 用户
 * SecurityManager 管理所有用户
 * Realm 连接数据
 *
 * @date 21-03-10
 * @author zjh
 */
@Configuration
public class ShiroConfig {

    /**
     * ShiroFilterFactoryBean
     * anon: 无需认证就可以访问
     * authc: 必须认证了才能访问
     * user: 必须拥有 记住我 功能才能用
     * perms: 拥有对某个资源的权限才能访问
     * roles: 拥有某个角色权限才能访问
     * @param defaultWebSecurityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);
        //设置登录的请求
        bean.setLoginUrl("/login");
        //设置未授权页面
        bean.setUnauthorizedUrl("/noauth");

        //添加自定义过滤器
        Map<String, Filter> filters = bean.getFilters();
        filters.put("authc",new ShiroUserFilter());
        filters.put("roles",new ShiroRoleFilter());
        bean.setFilters(filters);

        //添加shiro的内置过滤器
        Map<String, String> filterMap = new LinkedHashMap<>();
        //shiro放行swagger资源
        filterMap.put("/swagger-ui.html", "anon");
        filterMap.put("/swagger-resources/**", "anon");
        filterMap.put("/v2/**", "anon");
        filterMap.put("/webjars/**", "anon");
        //filterMap.put("/**","anon");
        filterMap.put("/backend/**","roles[teacher]");
        filterMap.put("/admin/ranking/**","roles[admin,teacher]");
        filterMap.put("/admin/**","roles[admin]");
        filterMap.put("/drurid/**","roles[admin]");
        filterMap.put("/student/**","roles[student,submit,appeal]");
        filterMap.put("/student2/**","roles[student,submit,appeal]");
        //filterMap.put("/**","anon");
        //注销
        filterMap.put("/logout","logout");
        //所有接口无需认证就可以访问
        //filterMap.put("/**","anon");
        bean.setFilterChainDefinitionMap(filterMap);

        return bean;
    }

    //DafaultWebSecurityManager
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDafaultWebSecurityManager(
            @Qualifier("userRealm") UserRealm userRealm,
            @Qualifier("RememberMeManager") RememberMeManager rememberMeManager,
            @Qualifier("sessionManager")  DefaultWebSessionManager sessionManager,
            @Qualifier("cacheManager") RedisCacheManager cacheManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联UserRealm
        securityManager.setRealm(userRealm);
        //关联RememberMeManager
        securityManager.setRememberMeManager(rememberMeManager);
        //关联自定义会话管理器
        securityManager.setSessionManager(sessionManager);
        //关联自定义redis缓存管理器
        securityManager.setCacheManager(cacheManager);
        return securityManager;
    }

    /**
     * 创建realm对象，需要自定义类
     * @param matcher 密码匹配器
     * @return
     */
    @Bean
    public UserRealm userRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher) {
        UserRealm userRealm = new UserRealm();
        //密码匹配器
        userRealm.setCredentialsMatcher(matcher);
        return userRealm;
    }


    //记住我
    @Bean
    public CookieRememberMeManager RememberMeManager() {
        CookieRememberMeManager manager = new CookieRememberMeManager();
        manager.setCookie(rememberMeCookie());
        //进行base64加密
        manager.setCipherKey(Base64.getDecoder().decode("3AvVhmFLUs0KTA3Kprsdag=="));
        return manager;
    }

    //cookie管理
    @Bean
    public SimpleCookie rememberMeCookie() {
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        //cookie有效时间 7天
        cookie.setMaxAge(3600 * 24 * 7);
        return cookie;
    }

    /**
     * 密码匹配器
     * @return HashedCredentialsMatcher
     */
    @Bean("hashedCredentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        // 设置哈希算法名称
        matcher.setHashAlgorithmName("MD5");
        // 设置哈希迭代次数
        matcher.setHashIterations(1024);
        // 设置存储凭证十六进制编码,false则采取base64
        matcher.setStoredCredentialsHexEncoded(true);
        return matcher;
    }

    /*****************************************shiro-redis-session*******************************************/
    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    @Value("${spring.redis.password}")
    private String password;

    @Value("${spring.redis.timeout}")
    private int timeout;

    @Value("${spring.redis.jedis.pool.max-active}")
    private int maxTotal;

    @Value("${spring.redis.jedis.pool.max-wait}")
    private long maxWaitMillis;

    @Value("${spring.redis.jedis.pool.max-idle}")
    private int maxIdle;

    @Value("${spring.redis.jedis.pool.min-idle}")
    private int minIdle;

    /**
     * redis控制器
     * @return
     */
    @Bean("redisManager")
    public RedisManager getRedisManager() {
        RedisManager redisManager = new RedisManager();
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(maxTotal);
        jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMinIdle(minIdle);
        JedisPool jedisPool = new JedisPool(jedisPoolConfig,host,port,timeout);
        redisManager.setJedisPool(jedisPool);
//        redisManager.setHost(host);
//        redisManager.setPort(port);
        if(StringUtils.isNotBlank(password)) {
            redisManager.setPassword(password);
        }
        return redisManager;
    }

    /**
     * sessionDao
     */
    @Bean("redisSessionDAO")
    public RedisSessionDAO getRedisSessionDAO(@Qualifier("redisManager") RedisManager redisManager) {
        RedisSessionDAO sessionDAO = new RedisSessionDAO();
        sessionDAO.setRedisManager(redisManager);
        //session在redis中的保存时间-秒，最好大于session会话超时时间
        sessionDAO.setExpire(60 * 60 + 5);
        return sessionDAO;
    }

    /**
     * 会话管理器
     */
    @Bean("sessionManager")
    public DefaultWebSessionManager sessionManager(@Qualifier("redisSessionDAO") RedisSessionDAO redisSessionDAO) {
        CustomSessionManager sessionManager = new CustomSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO);
        //全局session超时时间-毫秒-2h
        sessionManager.setGlobalSessionTimeout(60 * 60 * 1000);
        //取消url后面的JSESSIONID
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }

    /**
     * 缓存管理器
     */
    @Bean("cacheManager")
    public RedisCacheManager cacheManager(@Qualifier("redisManager") RedisManager redisManager) {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager);
        //设置userId作为存储字段
        redisCacheManager.setPrincipalIdFieldName("userId");
        return redisCacheManager;
    }

    /**
     * 开启对shiro注解的支持
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}
